#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "cups/cups.h"
#include "cups/array.h"

#define BUFFER_LEN 512

/*
** Tipos definidos
*/
typedef struct printer_info_s
{
	int id;
	char printer_name[128];
	char computer_name[128];
	char server_name[128];
	char user_name[32];
	char document[1024];
	char format[128];
	int status;
	int size;
	int total_pages;
	int pages_printed;
	time_t creation_time;
	time_t processing_time;
	time_t completed_time;
}printer_info_t;

typedef enum tipo_atualizacao_e
{
	taINSERT = 1,
	taUPDATE,
	taDELETE
}tipo_atualizacao_t;

/*
** Declaracao de variaveis globais
*/
char *username;
char servername[] = "127.0.0.1";
char hostname[128];
unsigned int portno = 631;
FILE *pfile_log;
cups_array_t *printer_info_array;

/*
** Prototipos
*/
void processarNotificacao(printer_info_t *ppit);
void *conectarServidor(void *lparam);
int jobcmp(void *primeiro, void *segundo, void *lparam);
void atualizarBanco(printer_info_t *ppit, tipo_atualizacao_t ta);

int main(void)
{
	/*
	** Variaveis para controle da impressao
	*/
	int total_jobs, i;
	pid_t pid, sid;
	cups_job_t *cjobs;
	printer_info_t *pit;

	/*
	** cria o processo filho
	*/
	pid = fork();
	if(pid < 0){
		printf("EXIT_FAILURE\n");
		exit(EXIT_FAILURE);
	}
	if(pid > 0) exit(EXIT_SUCCESS);

        /*
	** Muda o modo de mascara do arquivo
	*/
        umask(0);

	/*
	** Abre arquivo de log
	*/
	pfile_log = fopen("/gimpod.log", "w");

	/*
	** Pega o nome e ip do computador
	*/
	char name[128];
	memset(name, '\0', sizeof(name));
	memset(hostname, '\0', sizeof(hostname));
	if(!gethostname(name, sizeof(name))){
		struct hostent *ht;
		struct in_addr h_addr;
		if((ht = gethostbyname(name)) != NULL){
			h_addr.s_addr = *((unsigned long *)ht->h_addr_list[0]);
			strcpy(hostname, inet_ntoa(h_addr));
		}
	}
	//printf("hostname: %s\n", hostname);

        /*
	** Cria um novo SID (Session ID) para o processo filho
	*/
        sid = setsid();
        if(sid < 0){
		fprintf(pfile_log, "Erro funcao - setsid()\n");
		fclose(pfile_log);
                exit(EXIT_FAILURE);
        }

        /*
	** Altera o diretorio de trabalho
	*/
        if((chdir("/")) < 0){
		fprintf(pfile_log, "Erro alterando diretorio de trabalho\n");
		fclose(pfile_log);
                exit(EXIT_FAILURE);
        }
        
        /*
	** Fecha os descritores dos arquivos padroes
	* /
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);*/

	/*
	** Pega usuario logado
	*/
	username = getenv("USERNAME");

	printer_info_array = cupsArrayNew(jobcmp, NULL);
	for(;;){
		/*
		** Pega o total de impressoes
		*/
		total_jobs = cupsGetJobs(&cjobs, NULL, 0, -1);
		for(i=0; i < total_jobs; i++){
			if(strcmp(username, (cjobs+i)->user)) continue;
			pit = (printer_info_t *)malloc(sizeof(printer_info_t));
			pit->id = (cjobs+i)->id;
			strcpy(pit->printer_name, (cjobs+i)->dest); 
			strcpy(pit->computer_name, hostname);
			strcpy(pit->server_name, servername);
			strcpy(pit->user_name, (cjobs+i)->user);
			strcpy(pit->document, (cjobs+i)->title);
			strcpy(pit->format, (cjobs+i)->format);
			pit->status = (cjobs+i)->state;
			pit->size = (cjobs+i)->size * 1000;
			pit->total_pages = 1;
			pit->pages_printed = 0;
			pit->creation_time = (cjobs+i)->creation_time;
			pit->processing_time = (cjobs+i)->processing_time;
			pit->completed_time = (cjobs+i)->completed_time;
			if(!cupsArrayFind(printer_info_array, pit)) cupsArrayAdd(printer_info_array, pit);
			else free(pit);
			//printf("Id:%d titulo:%s usuario:%s estado:%d tamanho: %d destino: %s\n", (cjobs+i)->id, (cjobs+i)->title, (cjobs+i)->user, (cjobs+i)->state, (cjobs+i)->size, (cjobs+i)->dest);
			//ret_thread = pthread_create(&thread, NULL, processaNotificacao, (void *)(cjobs + i));
			//sleep(2);
		}
		for(pit = (printer_info_t *)cupsArrayFirst(printer_info_array);
		    pit; 
		    pit = (printer_info_t *)cupsArrayNext(printer_info_array)){
			printf("Percorre array - id: %d titulo: %s tamanho: %d criacao: (%s)\n", pit->id, pit->document, pit->size, ctime(&pit->creation_time));
			//processarNotificacao(pit);
			//cupsArrayRemove(printer_info_array, pit);
			//free(pit);
		}
		//printf("-----------  fim  ---------------\n");
		fflush(pfile_log);
		/*
		** Limpa memoria alocada
		*/
		if(total_jobs) cupsFreeJobs(total_jobs, cjobs);

		/*
		** Aguarda 2 segundo
		*/
		sleep(3);
	}
	fclose(pfile_log);
	return 0;
}

void processarNotificacao(printer_info_t *ppit)
{
	switch(ppit->status){
		case IPP_JOB_PENDING:
		case IPP_JOB_PROCESSING:
			atualizarBanco(ppit, taINSERT);
			break;
		case IPP_JOB_HELD:
			atualizarBanco(ppit, taUPDATE);
			break;
		case IPP_JOB_STOPPED:
		case IPP_JOB_CANCELED:
		case IPP_JOB_ABORTED:
		case IPP_JOB_COMPLETED:
			atualizarBanco(ppit, taUPDATE);
			cupsArrayRemove(printer_info_array, ppit);
			free(ppit);
			break;
	}

//	printf("Thread: Id:%d titulo:%s formato: %s usuario:%s estado:%d tamanho: %d destino: %s criacao: %d/%d/%d\n", pit.id, pit.document, pit.format, pit.user_name, pit.status, pit.size, pit.printer_name, t->tm_mday, t->tm_mon, t->tm_year + 1900);
/*	
	cups_dest_t *dests, *ret;
	int i;
	int num_dests = cupsGetDests(&dests);
	for(i=0; i<num_dests; i++){
		printf("destino: %s\t%d\n", (dests+i)->name, (dests+i)->num_options);
	}

	for(i=0; i<dests->num_options; i++){
		printf("opcao: %s\tvalor: %s\n", (dests->options+i)->name, (dests->options+i)->value);
	}
*/
/*
	if((ret=cupsGetDest(NULL, NULL, &num_dests, dest)))
		printf("destino: %s\t%s\t%d\n", dest->name, ret->name, ret->num_options);
/ *
	cups_option_t cot;
	char *ret;
	cot.name = "PageSize";
	cot.value = "teste";
	ret = (char *)cupsGetOption("PageSize", 1, &cot);
	printf("cupsGetOption = %s\n", cot.value);*/
}

void *conectarServidor(void *lparam)
{
	char buffer[BUFFER_LEN];
	int sock;
	struct sockaddr_in sin;
	struct hostent *host;

	/*
	** Abre conexao com o servidor
	*/
	sock = socket(AF_INET, SOCK_STREAM, 0);
	host = gethostbyname(servername);
	memcpy(&sin.sin_addr.s_addr, host->h_addr, host->h_length);
	sin.sin_family = AF_INET;
	sin.sin_port = htons(portno);

	if(connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0){
		printf("Erro conectando %s:%d\n", servername, portno);
		exit(1);
	}
	sleep(1);
	//for(;;){
	//	if(!printFromSocket
	close(sock);
}

int jobcmp(void *primeiro, void *segundo, void *lparam)
{
	return (((printer_info_t *)primeiro)->id - ((printer_info_t *)segundo)->id);
}

void atualizarBanco(printer_info_t *ppit, tipo_atualizacao_t ta)
{
	static char comando[4096];
	struct tm *tm_creation = localtime(&ppit->creation_time),
	          *tm_processing = localtime(&ppit->processing_time),
	          *tm_completed = localtime(&ppit->completed_time);

	/*
	** Variaveis para controle da thread
	*/
	pthread_t thread;
	int ret_thread;
/*
	switch(ta){
		case taINSERT:
			snprintf(comando, sizeof(comando), "INSERT INTO TBL_IMPRESSAO(NU_IMPRESSAO, 
		case taUPDATE:
	}
*/
/*
	int id;
	char printer_name[128];
	char computer_name[128];
	char server_name[128];
	char user_name[32];
	char document[1024];
	char format[128];
	int status;
	int size;
	int total_pages;
	int pages_printed;
	time_t creation_time;
	time_t processing_time;
	time_t completed_time;
*/
	ret_thread = pthread_create(&thread, NULL, conectarServidor, (void *)ppit);
	sleep(1);
}

int paginasDoc(int jobID)
{
	char nomeArquivo[260];
	FILE *fp;

	//sprintf(nomeArquivo, "%sd%05d.001", jobID);
}
